package aole.mindfx.profile;

import java.util.ArrayList;
import java.util.HashSet;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import aole.mindfx.model.AppStructure;
import aole.mindfx.model.MindFXAttribute;
import aole.mindfx.model.MindFXCall;
import aole.mindfx.model.MindFXCallAttribute;
import aole.mindfx.model.MindFXCallParam;
import aole.mindfx.model.MindFXClass;
import aole.mindfx.model.MindFXMethod;
import aole.mindfx.model.MindFXParam;

public class CppProfiler implements Profiler {
	private static String				types[]	= { "int", "char", "bool",
			"float", "double", "long", "std::string", "std::map" };

	private ObservableList< String >	atypes;

	private ObservableList< String >	mtypes;

	private AppStructure				structure;

	public CppProfiler( AppStructure as ) {
		structure = as;

		atypes = FXCollections.observableArrayList( types );
		for( MindFXClass c: as.getAllClasses() )
			atypes.add( c.getName() );
		mtypes = FXCollections.observableArrayList( types );
		mtypes.add( "void" );
		for( MindFXClass c: as.getAllClasses() )
			mtypes.add( c.getName() );
	}

	@Override
	public ObservableList< String > getAttributeTypes() {
		return atypes;
	}

	@Override
	public ObservableList< String > getAttributeScopes() {
		ObservableList< String > ol = FXCollections.observableArrayList(
				"private", "protected", "public" );
		return ol;
	}

	@Override
	public ObservableList< String > getMethodTypes() {
		return mtypes;
	}

	@Override
	public ObservableList< String > getMethodScopes() {
		ObservableList< String > ol = FXCollections.observableArrayList(
				"private", "public" );
		return ol;
	}

	@Override
	public void typeAdded( String type ) {
		atypes.add( type );
		mtypes.add( type );
	}

	@Override
	public MindFXClass getFXClass( String type ) {
		return structure.getFXClass( type );
	}

	@Override
	public Pane getFXClassCode( MindFXClass c ) {
		VBox pane = new VBox( 2 );

		ArrayList< MindFXAttribute > priAttri = new ArrayList<>();
		ArrayList< MindFXAttribute > pubAttri = new ArrayList<>();
		ArrayList< MindFXMethod > priMeth = new ArrayList<>();
		ArrayList< MindFXMethod > pubMeth = new ArrayList<>();
		HashSet< String > includes = new HashSet<>();

		// get private/public attribues and include files
		for( MindFXAttribute a: c.getAttributes() ) {
			switch( a.getScope() ) {
			case "private":
				priAttri.add( a );
				break;
			case "public":
				pubAttri.add( a );
				break;
			}
			MindFXClass inc = structure.getFXClass( a.getType() );
			if( inc != null ) includes.add( inc.getHeaderFile() );
		}

		// get private/public methods and include files
		for( MindFXMethod a: c.getMethods() ) {
			switch( a.getScope() ) {
			case "private":
				priMeth.add( a );
				break;
			case "public":
				pubMeth.add( a );
				break;
			}
			// get include files
			MindFXClass inc = structure.getFXClass( a.getType() );
			if( inc != null ) includes.add( inc.getHeaderFile() );
		}

		// print header class
		pane.getChildren().add( new Label( "// " + c.getHeaderFile() ) );
		// includes
		for( String inc: includes ) {
			pane.getChildren().add( new Label( "#include \"" + inc + "\"" ) );
		}
		// show class name
		HBox box1 = new HBox( 2 );
		box1.getChildren().add( new Label( "class " ) );
		box1.getChildren().add( c.getCodeButton() );
		box1.getChildren().add( new Label( " {" ) );
		pane.getChildren().add( box1 );

		// private variables
		pane.getChildren().add( new Label( "private:" ) );
		for( MindFXAttribute a: priAttri ) {
			HBox box = new HBox( 2 );
			box.getChildren().add( new Label( "\t" ) );
			box.getChildren().add( getAttributeDefinition( a ) );
			box.getChildren().add( new Label( ";" ) );
			pane.getChildren().add( box );
		}
		// public variables
		pane.getChildren().add( new Label( "public:" ) );
		for( MindFXAttribute a: pubAttri ) {
			HBox box = new HBox( 2 );
			box.getChildren().add( new Label( "\t" ) );
			box.getChildren().add( getAttributeDefinition( a ) );
			box.getChildren().add( new Label( ";" ) );
			pane.getChildren().add( box );
		}
		// private members
		pane.getChildren().add( new Label( "private:" ) );
		for( MindFXMethod m: priMeth ) {
			HBox box = new HBox( 2 );
			box.getChildren().add( new Label( "\t" ) );
			box.getChildren().add( getMethodDefinition( m ) );
			box.getChildren().add( new Label( ";" ) );
			pane.getChildren().add( box );
		}
		// public members
		pane.getChildren().add( new Label( "public:" ) );
		for( MindFXMethod m: pubMeth ) {
			HBox box = new HBox( 2 );
			box.getChildren().add( new Label( "\t" ) );
			box.getChildren().add( getMethodDefinition( m ) );
			box.getChildren().add( new Label( ";" ) );
			pane.getChildren().add( box );
		}
		pane.getChildren().add( new Label( "}\n" ) );

		// print source class
		pane.getChildren().add( new Label( "// " + c.getSourceFile() ) );
		pane.getChildren().add(
				new Label( "#include \"" + c.getHeaderFile() + "\"\n" ) );

		for( MindFXMethod m: c.getMethods() ) {
			pane.getChildren().add( new Label( getMethodDecleration( c, m ) ) );
		}
		pane.getChildren().add( new Label() );

		return pane;
	}

	private Pane getAttributeDefinition( MindFXAttribute a ) {
		HBox box = new HBox( 2 );
		MindFXClass c = structure.getFXClass( a.getType() );
		if( c != null )
			box.getChildren().add( c.getCodeButton() );
		else box.getChildren().add( new Label( a.getType() ) );

		box.getChildren().add( a.getCodeButton() );

		return box;
	}

	private Pane getMethodDefinition( MindFXMethod m ) {
		HBox box = new HBox( 2 );
		MindFXClass c = structure.getFXClass( m.getType() );
		if( c != null )
			box.getChildren().add( c.getCodeButton() );
		else box.getChildren().add( new Label( m.getType() ) );

		box.getChildren().add( m.getCodeButton() );

		return box;
	}

	private String getMethodDecleration( MindFXClass c, MindFXMethod m ) {
		String ps = m.getType();
		int p = m.getPointers();
		for( int i = 0; i < p; i++ )
			ps += "*";

		String s = ps + " " + c.getName() + "::" + m.getName() + "() {\n";
		for( MindFXCall call: m.getFXCalls() ) {
			s += "\t";
			String calltype = call.getName();
			switch( calltype ) {
			case "this":
				break;
			case "attribute":
				MindFXAttribute satt = ((MindFXCallAttribute) call)
						.getAttribute();
				s += satt.getName();
				if( satt.getPointers() > 0 )
					s += "->";
				else s += ".";
				break;
			case "parameter":
				MindFXParam sp = ((MindFXCallParam) call).getParameter();
				s += sp.getName();
				if( sp.getPointers() > 0 )
					s += "->";
				else s += ".";
				break;
			}
			MindFXMethod met = call.getMethod();
			if( met == null )
				s += call.getMethod() + "();//TODO: create method\n";
			else s += met.getName() + "();\n";
		}
		s += "\t//TODO: method stub\n}";

		return s;
	}

	@Override
	public void typeRemoved( String name ) {
		mtypes.remove( name );
		atypes.remove( name );
	}

}
